﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Hook.Primitives;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Hook.Graphics;
using Microsoft.Xna.Framework.Storage;
using System.IO;
using Microsoft.Xna.Framework.GamerServices;
using System.Xml.Serialization;
using Hook.Environmental.Props;

namespace Hook.Environmental
{
    public class PropEditor
    {
        public const double MOUSETIME = .5;
        public const int PIXELTOLERANCE = 7;

        public const int POINT = 0;
        public const int NAME = 1;
        public const int MASS = 2;
        public const int INERTIA = 3;
        public const int COLLISION = 4;

        public LineGrid Grid;
        public Camera Cam;
        public bool GridSizeKeyed, LeftMouseDown, RightMouseDown, PointBeingDragged, PropSaveRequested, PropLoadRequested;
        //public string PropMass, PropInertia, PropCollision;
        public int DragPointIndex, PropEditorFocus;
        public double MouseTimer;
        public IAsyncResult SyncResult;
        public TextField NameField, MassField, InertiaField, CollisionField;

        public Prop CurrentProp;

        public PropEditor()
        {
            Cam = new Camera();

            Grid = new LineGrid();
            GridSizeKeyed = false;
            MouseTimer = MOUSETIME;

            NameField = new TextField(new Vector3(-Game1.RESOLUTION.X / 2 + 10, -Game1.RESOLUTION.Y / 2 + 5, 0));
            MassField = new TextField(new Vector3(-Game1.RESOLUTION.X / 2 + 10, -Game1.RESOLUTION.Y / 2 + 25, 0));
            InertiaField = new TextField(new Vector3(-Game1.RESOLUTION.X / 2 + 10, -Game1.RESOLUTION.Y / 2 + 45, 0));
            CollisionField = new TextField(new Vector3(-Game1.RESOLUTION.X / 2 + 10, -Game1.RESOLUTION.Y / 2 + 65, 0));

            CurrentProp = new Box(Vector3.Zero, Vector3.Zero);
            CurrentProp.Name = "TestProp.txt";
            NameField.Text = CurrentProp.Name;
            MassField.Text = CurrentProp.Mass.ToString();
            InertiaField.Text = CurrentProp.Inertia.ToString();
            CurrentProp.CollisionLayer.Add(0);
        }

        public bool MouseOnScreen()
        {
            int MX = Mouse.GetState().X;
            int MY = Mouse.GetState().Y;

            if(MX < Game1.RESOLUTION.X &&
                MX > 0 &&
                MY < Game1.RESOLUTION.Y &&
                MY > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public void AddPoint(Vector3 Point)
        {
            CurrentProp.Frame.Offsets.Add(Point - CurrentProp.Frame.ReferencePoint.Position);
            CurrentProp.Frame.SetPoints();
        }

        public void RemovePoint(int PointIndex)
        {
            CurrentProp.Frame.Offsets.RemoveAt(PointIndex);
            CurrentProp.Frame.SetPoints();
        }

        public void SetPropData()
        {
            CurrentProp.Name = NameField.Text;
            try
            {
                CurrentProp.Mass = float.Parse(MassField.Text);
            }
            catch (Exception E)
            {
                //Mass input format incorrect.
                Console.WriteLine(E);
            }
            try
            {
                CurrentProp.Inertia = float.Parse(InertiaField.Text);
            }
            catch (Exception E)
            {
                //Inertia input format incorrect.
                Console.WriteLine(E);
            }
            //CurrentProp.CollisionLayer = int.
        }

        public void HandleInput()
        {
            KeyboardState kbs = Keyboard.GetState();
            MouseState ms = Mouse.GetState();

            //Event left mouse button is pressed.
            if(ms.LeftButton == ButtonState.Pressed && MouseOnScreen() && !LeftMouseDown)
            {
                if (MouseOverName())
                {
                    PropEditorFocus = NAME;
                    NameField.Selected = true;
                    MassField.Selected = false;
                    InertiaField.Selected = false;
                    CollisionField.Selected = false;
                    SetPropData();
                }
                else if (MouseOverMass())
                {
                    PropEditorFocus = MASS;
                    NameField.Selected = false;
                    MassField.Selected = true;
                    InertiaField.Selected = false;
                    CollisionField.Selected = false;
                    SetPropData();
                }
                else if (MouseOverInertia())
                {
                    PropEditorFocus = INERTIA;
                    NameField.Selected = false;
                    MassField.Selected = false;
                    InertiaField.Selected = true;
                    CollisionField.Selected = false;
                    SetPropData();
                }
                else if (MouseOverCollision())
                {
                    PropEditorFocus = COLLISION;
                    NameField.Selected = false;
                    MassField.Selected = false;
                    InertiaField.Selected = false;
                    CollisionField.Selected = true;
                    SetPropData();
                }
                else if (MouseOverGravity())
                {
                    CurrentProp.GravityOn = !CurrentProp.GravityOn;
                    SetPropData();
                }
                else
                {
                    PropEditorFocus = POINT;

                    NameField.Selected = false;
                    MassField.Selected = false;
                    InertiaField.Selected = false;
                    CollisionField.Selected = false;
                    SetPropData();

                    int PointIndex = MouseOverPropPoint();
                    if (PointIndex == -1)
                    {
                        AddPoint(new Vector3(ms.X - Game1.RESOLUTION.X / 2, ms.Y - Game1.RESOLUTION.Y / 2, 0));
                    }
                    else
                    {
                        PointBeingDragged = true;
                        DragPointIndex = PointIndex;
                    }
                }

                LeftMouseDown = true;

                MouseTimer = MOUSETIME;
            }
            //Event left mouse button is released.
            if (ms.LeftButton == ButtonState.Released && MouseOnScreen() && LeftMouseDown)
            {
                if (PointBeingDragged)
                {
                    PointBeingDragged = false;
                }

                LeftMouseDown = false;
            }
            //Set drag point position.
            if (PointBeingDragged)
            {
                Vector3 Offset = new Vector3(ms.X - Game1.RESOLUTION.X / 2, ms.Y - Game1.RESOLUTION.Y / 2, 0) - CurrentProp.Frame.ReferencePoint.Position;
                CurrentProp.Frame.Offsets[DragPointIndex] = Offset;
            }
            //Event right mouse button is pressed.
            if (ms.RightButton == ButtonState.Pressed && MouseOnScreen() && !RightMouseDown)
            {
                int PointIndex = MouseOverPropPoint();
                if (PointIndex != -1)
                {
                    RemovePoint(PointIndex);
                }

                RightMouseDown = true;

                MouseTimer = MOUSETIME;
            }
            //Event right mouse button is released.
            if (ms.RightButton == ButtonState.Released && MouseOnScreen() && RightMouseDown)
            {
                RightMouseDown = false;
            }

            if(kbs.IsKeyDown(Keys.OemOpenBrackets) && !GridSizeKeyed)
            {
                Grid.SetFactor(new Vector2(Grid.Factor.X / 2, Grid.Factor.Y / 2));
                GridSizeKeyed = true;
            }
            else if(kbs.IsKeyDown(Keys.OemCloseBrackets) && !GridSizeKeyed)
            {
                Grid.SetFactor(new Vector2(Grid.Factor.X * 2, Grid.Factor.Y * 2));
                GridSizeKeyed = true;
            }

            if(kbs.IsKeyUp(Keys.OemOpenBrackets) && kbs.IsKeyUp(Keys.OemCloseBrackets) && GridSizeKeyed)
            {
                GridSizeKeyed = false;
            }

            if(kbs.IsKeyDown(Keys.OemMinus) && !PropSaveRequested)
            {
                // Set the request flag
                if(!Guide.IsVisible)
                {
                   PropSaveRequested = true;
                   SyncResult = Guide.BeginShowStorageDeviceSelector(PlayerIndex.One, null, null);
                }
            }
            if(kbs.IsKeyDown(Keys.OemPlus) && !PropLoadRequested)
            {
                //LoadPropData("PropSaves/TestProp.txt");

                // Set the request flag
                if(!Guide.IsVisible)
                {
                    PropLoadRequested = true;
                    SyncResult = Guide.BeginShowStorageDeviceSelector(PlayerIndex.One, null, null);
                }
            }
        }

        public void Update(GameTime gt)
        {
            if(MouseTimer > 0)
            {
                MouseTimer -= gt.ElapsedGameTime.TotalSeconds;
                if(MouseTimer < 0)
                {
                    MouseTimer = 0;
                }
            }

            HandleInput();

            NameField.Update(gt);
            MassField.Update(gt);
            InertiaField.Update(gt);
            CollisionField.Update(gt);

            if((PropSaveRequested) && (SyncResult.IsCompleted))
            {
                StorageDevice Device = Guide.EndShowStorageDeviceSelector(SyncResult);
                if(Device != null && Device.IsConnected)
                {
                    SavePropData(Device, NameField.Text);
                }
                // Reset the request flag
                PropSaveRequested = false;
            }

            if((PropLoadRequested) && (SyncResult.IsCompleted))
            {
                StorageDevice Device = Guide.EndShowStorageDeviceSelector(SyncResult);
                if(Device != null && Device.IsConnected)
                {
                    LoadPropData(Device, NameField.Text);
                }
                // Reset the request flag
                PropLoadRequested = false;
            }

            CurrentProp.Update(gt);

            //Grid.Update(gt);
        }
        public void Draw(SpriteBatch sb)
        {
            CurrentProp.Draw(sb, Cam);

            //Grid.Draw(sb, Cam);

            NameField.Draw(sb);
            MassField.Draw(sb);
            InertiaField.Draw(sb);
            CollisionField.Draw(sb);
            sb.DrawString(Game1.GameFont, "Gravity On: " + CurrentProp.GravityOn, new Vector2(10,90), Color.Black);
        }

        /// <summary>
        /// Returns index of a point in current prop close to the mouse cursor.
        /// </summary>
        public int MouseOverPropPoint()
        {
            float MX = Mouse.GetState().X - Game1.RESOLUTION.X / 2;
            float MY = Mouse.GetState().Y - Game1.RESOLUTION.Y / 2;
            int PIndex = -1;
            for (int p = 0; p < CurrentProp.Frame.Points.Count; p++)
            {
                PolyPoint P = CurrentProp.Frame.Points[p];
                if (MX < P.Position.X + PIXELTOLERANCE
                    && MX > P.Position.X - PIXELTOLERANCE
                    && MY < P.Position.Y + PIXELTOLERANCE
                    && MY > P.Position.Y - PIXELTOLERANCE)
                {
                    PIndex = p;
                }
            }
            return PIndex;
        }

        public bool MouseOverName()
        {
            float MX = Mouse.GetState().X;
            float MY = Mouse.GetState().Y;
            if (MX < 200 && MX > 10 && MY < 30 && MY > 10)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public bool MouseOverMass()
        {
            float MX = Mouse.GetState().X;
            float MY = Mouse.GetState().Y;
            if (MX < 200 && MX > 10 && MY < 50 && MY > 30)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public bool MouseOverInertia()
        {
            float MX = Mouse.GetState().X;
            float MY = Mouse.GetState().Y;
            if (MX < 200 && MX > 10 && MY < 70 && MY > 50)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public bool MouseOverCollision()
        {
            float MX = Mouse.GetState().X;
            float MY = Mouse.GetState().Y;
            if (MX < 200 && MX > 10 && MY < 90 && MY > 70)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public bool MouseOverGravity()
        {
            float MX = Mouse.GetState().X;
            float MY = Mouse.GetState().Y;
            if (MX < 200 && MX > 10 && MY < 110 && MY > 90)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public void LoadPropData(StorageDevice Device, string FileName)
        {
            StorageContainer Container = Device.OpenContainer("StorageTest");

            // Get the path of the save game.
            //FileName = Path.Combine(Container.Path, FileName);
            FileName = "Props/" + FileName;

            if (File.Exists(FileName))
            {
                List<string> Lines = new List<string>();
                StreamReader SR = new StreamReader(FileName);

                string CurrentLine = SR.ReadLine();
                while (CurrentLine != null)
                {
                    Lines.Add(CurrentLine);
                    CurrentLine = SR.ReadLine();
                }

                for (int s = 0; s < Lines.Count; s++)
                {
                    if (Lines[s].StartsWith("Offsets = "))
                    {
                        List<Vector3> Offsets = new List<Vector3>();
                        int NumOfOffsets = int.Parse(Lines[s].Substring("Offsets = ".Length));
                        for (int o = 0; o < NumOfOffsets; o++)
                        {
                            Vector3 Offset = new Vector3();
                            int XLoc, YLoc, ZLoc;
                            XLoc = Lines[s + o + 1].IndexOf('X');
                            YLoc = Lines[s + o + 1].IndexOf('Y');
                            ZLoc = Lines[s + o + 1].IndexOf('Z');
                            Offset.X = float.Parse(Lines[s + o + 1].Substring(XLoc + 2, YLoc - XLoc - 3));
                            Offset.Y = float.Parse(Lines[s + o + 1].Substring(YLoc + 2, ZLoc - YLoc - 3));
                            Offset.Z = float.Parse(Lines[s + o + 1].Substring(ZLoc + 2, Lines[s + o + 1].Length - ZLoc - 3));
                            Offsets.Add(Offset);
                        }
                        CurrentProp.Frame.SetPoints(Offsets);
                        s += NumOfOffsets;
                    }
                    else if (Lines[s].StartsWith("Mass = "))
                    {
                        CurrentProp.Mass = float.Parse(Lines[s].Substring("Mass = ".Length));
                        MassField.Text = CurrentProp.Mass.ToString();
                    }
                    else if (Lines[s].StartsWith("Inertia = "))
                    {
                        CurrentProp.Inertia = float.Parse(Lines[s].Substring("Inertia = ".Length));
                        InertiaField.Text = CurrentProp.Inertia.ToString();
                    }
                    else if (Lines[s].StartsWith("CollisionLayer = "))
                    {
                        List<int> Layers = new List<int>();
                        int NumOfLayers = int.Parse(Lines[s].Substring("CollisionLayer = ".Length));
                        for (int l = 0; l < NumOfLayers; l++)
                        {
                            int Layer;
                            Layer = int.Parse(Lines[s + l + 1]);
                            Layers.Add(Layer);
                        }
                        CurrentProp.CollisionLayer = Layers;

                        CollisionField.Text = "";
                        foreach (int c in CurrentProp.CollisionLayer)
                            CollisionField.Text += c + " ";

                        s += NumOfLayers;
                    }
                    else if (Lines[s].StartsWith("GravityOn = "))
                    {
                        CurrentProp.GravityOn = bool.Parse(Lines[s].Substring("GravityOn = ".Length));
                    }
                }
            }
        }

        public void SavePropData(StorageDevice Device, string FileName)
        {
            // Open a storage container.
            StorageContainer Container = Device.OpenContainer("StorageTest");

            // Get the path of the save game.
            //FileName = Path.Combine(Container.Path, FileName);
            FileName = "Props/" + FileName;

            try
            {
                StreamWriter SW = new StreamWriter(FileName);

                SW.WriteLine("Offsets = " + CurrentProp.Frame.Points.Count);
                for (int p = 0; p < CurrentProp.Frame.Points.Count; p++)
                {
                    SW.WriteLine(CurrentProp.Frame.Offsets[p]);
                }
                SW.WriteLine("Mass = " + CurrentProp.Mass);
                SW.WriteLine("Inertia = " + CurrentProp.Inertia);
                SW.WriteLine("CollisionLayer = " + CurrentProp.CollisionLayer.Count);
                for (int c = 0; c < CurrentProp.CollisionLayer.Count; c++)
                {
                    SW.WriteLine(CurrentProp.CollisionLayer[c]);
                }
                SW.WriteLine("GravityOn = " + CurrentProp.GravityOn);

                SW.Close();
            }
            catch (Exception E)
            {
                Console.WriteLine(E);
                //Stream Writer messed up.
            }

            // Dispose the container, to commit changes.
            Container.Dispose();
        }

        /*public static void SaveProp(StorageDevice Device)
        {
            // Open a storage container.
            StorageContainer Container = Device.OpenContainer("StorageTest");

            // Add the container path to our file name.
            string FileName = Path.Combine(Container.Path, "TestProp.sav");

            // Create a new file.
            if(!File.Exists(FileName))
            {
                FileStream FS = File.Create(FileName);
                FS.Close();
            }
            // Dispose the container, to commit the data.
            Container.Dispose();
        }*/

        /*public void LoadPropData(StorageDevice Device)
        {
            // Open a storage container.
            StorageContainer Container = Device.OpenContainer("StorageTest");

            // Get the path of the save game.
            string FileName = Path.Combine(Container.Path, "TestProp.sav");

            if(File.Exists(FileName))
            {
                // Open the file.
                FileStream Stream = File.Open(FileName, FileMode.Open);

                // Pull that stuff out!
                XmlSerializer Serializer = new XmlSerializer(typeof(PropData));
                PropData Data = (PropData)Serializer.Deserialize(Stream);

                CurrentProp.Frame = Data.Frame;

                // Close the file.
                Stream.Close();
            }

            // Dispose the container, to commit changes.
            Container.Dispose();
        }

        public void SavePropData(StorageDevice Device)
        {
            // Create the data to save.
            PropData Data = new PropData();
            Data.Frame = CurrentProp.Frame;

            // Open a storage container.
            StorageContainer Container = Device.OpenContainer("StorageTest");

            // Get the path of the save game.
            string FileName = Path.Combine(Container.Path, "TestProp.sav");

            // Open the file, creating it if necessary.
            FileStream Stream = File.Open(FileName, FileMode.Create);

            // Convert the object to XML data and put it in the stream.
            XmlSerializer Serializer = new XmlSerializer(typeof(PropData));
            Serializer.Serialize(Stream, Data);

            // Close the file.
            Stream.Close();

            // Dispose the container, to commit changes.
            Container.Dispose();
        }*/
    }
}
